Emberveil sourcegame/2006Scape Client/src/main/java/PetCacheUpdate.java
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.security.*;
/** Integrity-checked static game assets; no credentials or executable downloads. */
public final class PetCacheUpdate {
private static final String URL_BASE="https://downloads.emberveil.net/mobile/cache-20261019-pets/";
private static final String[][] FILES={{"main_file_cache.dat","efd1109d871bf9cc41b8390dc35b4079b77985c904d6c718875f4263b7533d28","26114538"},
{"main_file_cache.idx0","449736b66171453b5be67c3c1d4548cf250a24c40d7470fd3adbcd7567725f59","54"},
{"main_file_cache.idx1","ec5009813e5f07cbdf2fad45f944e8344a211ac1187cef72a84dfb1f356eadb9","355470"},
{"main_file_cache.idx2","58394a62e2f45102462d4dd3554df8b356708a458c5b63cd5c45658b55e901cf","9534"},
{"main_file_cache.idx3","1c76de27b1652ee9e05c59c797270d530d9d0d60f5b6fa56df78b504d95e73b3","3882"},
{"main_file_cache.idx4","b1543c88d74738f83c1a11cada71013fd4041c953ff2f4619caa7a40d30ea5d3","8424"}};
private PetCacheUpdate(){}
private static String hash(Path path)throws IOException {
try{MessageDigest md=MessageDigest.getInstance("SHA-256");try(InputStream in=Files.newInputStream(path)){byte[] buf=new byte[65536];int n;while((n=in.read(buf))!=-1)md.update(buf,0,n);}StringBuilder out=new StringBuilder();for(byte b:md.digest())out.append(String.format("%02x",b&255));return out.toString();}catch(NoSuchAlgorithmException ex){throw new IOException(ex);}
}
private static boolean valid(Path root,String[] file)throws IOException {Path p=root.resolve(file[0]);return Files.isRegularFile(p)&&Files.size(p)==Long.parseLong(file[2])&&hash(p).equals(file[1]);}
public static boolean complete(Path root)throws IOException {for(String[] file:FILES)if(!valid(root,file))return false;return true;}
public static synchronized Path prepare(String directory)throws IOException {
Path base=Paths.get(directory).toAbsolutePath().normalize();
if(complete(base)){System.setProperty("emberveil.cache",base.toString());return base;}
Path target=base.resolveSibling(base.getFileName()+"-20261019-pets");Files.createDirectories(target);
for(String[] file:FILES){
if(valid(target,file))continue;
Path pending=Files.createTempFile(target,"emberveil-cache-",".part");
try{
HttpURLConnection conn=(HttpURLConnection)new URL(URL_BASE+file[0]).openConnection();conn.setConnectTimeout(15000);conn.setReadTimeout(60000);conn.setInstanceFollowRedirects(false);
try{if(conn.getResponseCode()!=200)throw new IOException("Game file download returned "+conn.getResponseCode());long expected=Long.parseLong(file[2]),received=0;try(InputStream in=conn.getInputStream();OutputStream out=Files.newOutputStream(pending)){byte[] buf=new byte[65536];int n;while((n=in.read(buf))!=-1){received+=n;if(received>expected)throw new IOException("Unexpected game-file length");out.write(buf,0,n);}if(received!=expected)throw new IOException("Incomplete game-file download");}}finally{conn.disconnect();}
if(!hash(pending).equals(file[1]))throw new IOException("Game-file integrity check failed");
try{Files.move(pending,target.resolve(file[0]),StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING);}catch(AtomicMoveNotSupportedException ex){Files.move(pending,target.resolve(file[0]),StandardCopyOption.REPLACE_EXISTING);}
}finally{Files.deleteIfExists(pending);}
}
if(!complete(target))throw new IOException("Game-file update is incomplete");System.setProperty("emberveil.cache",target.toString());return target;
}
}